
# Set version and project name
cmake_minimum_required(VERSION 3.10)
project(ssd-sim)

# Validate build type
if (NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
  set(CMAKE_BUILD_TYPE "Release" CACHE STRING "Select CMake build type." FORCE)

  set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug" "Release")
endif ()

string (TOUPPER "${CMAKE_BUILD_TYPE}" uppercase_CMAKE_BUILD_TYPE)

if (CMAKE_BUILD_TYPE AND NOT uppercase_CMAKE_BUILD_TYPE MATCHES "^(DEBUG|RELEASE)$")
  message (FATAL_ERROR "Invalid value for CMAKE_BUILD_TYPE: ${CMAKE_BUILD_TYPE}")
endif ()

# Validate build path
# https://github.com/llvm/llvm-project/blob/master/llvm/CMakeLists.txt#L249
if (CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_CURRENT_BINARY_DIR AND NOT MSVC)
  message(FATAL_ERROR "In-source builds are not allowed.
Please create a directory and run cmake from there, passing the path
to this source directory as the last argument.
This process created the file `CMakeCache.txt' and the directory `CMakeFiles'.
Please delete them.")
endif ()

# Add options for sanitizer
option(USE_SANITIZER "Use sanitizer." OFF)

# Check MsCtrl is valid
if (NOT EXISTS "${PROJECT_SOURCE_DIR}/msctrl/CMakeLists.txt")
  message(FATAL_ERROR "Failed to find MsCtrl. \
Check you initialized submodules.")
endif ()

# Check pthread
find_package(Threads REQUIRED)

# Add subproject
add_subdirectory(${PROJECT_SOURCE_DIR}/msctrl
  ${CMAKE_BINARY_DIR}/msctrl
)

# Set include directories
include_directories(
  ${PROJECT_SOURCE_DIR}
  ${PROJECT_SOURCE_DIR}/msctrl
  ${DRAMPOWER_SOURCE_DIR}
)

# Make version
set(INPUT_VERSION_FILE "${PROJECT_SOURCE_DIR}/main/version.cc.in")
set(OUTPUT_VERSION_FILE "${CMAKE_CURRENT_BINARY_DIR}/version.cc")
set(PROJECT_DIRECTORY "${PROJECT_SOURCE_DIR}")
set(VERSION_TARGET "standalone_version")

include(${PROJECT_SOURCE_DIR}/msctrl/util/scripts/msctrl_version.cmake)

# Linker flags
set(LD_FLAGS "")

set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_C_STANDARD 11)
set(CMAKE_C_STANDARD_REQUIRED ON)

# Specify source files
set(SRC_DRIVER_NONE
  driver/none/none.cc
)
set(SRC_DRIVER_NVME
  driver/nvme/nvme.cc
  driver/nvme/prp.cc
  driver/nvme/queue.cc
)
set(SRC_IGL
  igl/block_io.cc
)
set(SRC_IGL_REQUEST
  igl/request/request_config.cc
  igl/request/request_generator.cc
)
set(SRC_IGL_TRACE
  igl/trace/trace_config.cc
  igl/trace/trace_replayer.cc
)
set(SRC_MAIN
  main/config_reader.cc
  main/config.cc
  main/engine.cc
  main/main.cc
  main/signal.cc
)
set(SRC_UTIL
  util/argparse.cc
  util/print.cc
  util/stopwatch.cc
)

# Source group for MSVC
SOURCE_GROUP("Source Files\\driver\\none" FILES ${SRC_DRIVER_NONE})
SOURCE_GROUP("Source Files\\driver\\nvme" FILES ${SRC_DRIVER_NVME})
SOURCE_GROUP("Source Files\\igl" FILES ${SRC_IGL})
SOURCE_GROUP("Source Files\\igl\\request" FILES ${SRC_IGL_REQUEST})
SOURCE_GROUP("Source Files\\igl\\trace" FILES ${SRC_IGL_TRACE})
SOURCE_GROUP("Source Files\\main" FILES ${SRC_MAIN})
SOURCE_GROUP("Source Files\\util" FILES ${SRC_UTIL})

# Define executable
add_executable(ssd-sim
  ${SRC_DRIVER_NVME}
  ${SRC_DRIVER_NONE}
  ${SRC_IGL}
  ${SRC_IGL_REQUEST}
  ${SRC_IGL_TRACE}
  ${SRC_MAIN}
  ${SRC_UTIL}
  ${OUTPUT_VERSION_FILE}
)

# Platform specific settings
if (MSVC)
  target_compile_definitions(ssd-sim PRIVATE -D_CRT_SECURE_NO_WARNINGS)
  target_compile_options(ssd-sim PRIVATE /wd4819)  # Surpress unicode warning
else ()
  # Check libunwind/libelf
  include(CheckIncludeFiles)

  check_include_files("libunwind.h" HAVE_UNWIND_H)
  check_include_files("elfutils/libdwfl.h" HAVE_LIBDWFL_H)

  if (NOT HAVE_UNWIND_H)
    message(FATAL_ERROR "Please install libunwind-dev or libunwind-devel.")
  endif ()
  if (NOT HAVE_LIBDWFL_H)
    message(FATAL_ERROR "Please install libdw-dev or elfutils-devel.")
  endif ()

  if (USE_SANITIZER)
    set(SANFLAG
      -fsanitize=address
      -fsanitize=undefined
      -fno-sanitize-recover=all
      -fsanitize=float-divide-by-zero
      -fsanitize=float-cast-overflow
      -fno-sanitize=null
      -fno-sanitize=alignment
    )
  else ()
    set(SANFLAG "")
  endif ()

  target_compile_options(ssd-sim PRIVATE -g -Wall -Wextra -Werror ${SANFLAG})
  target_link_libraries(ssd-sim ${SANFLAG})

  set(LD_FLAGS "-ldw -lunwind -lstdc++fs")
  set(CMAKE_CXX_FLAGS "-D__FILENAME__='\"$(subst ${PROJECT_SOURCE_DIR}/,,$(abspath $<))\"' ${CMAKE_CXX_FLAGS}")

  if (uppercase_CMAKE_BUILD_TYPE STREQUAL "DEBUG")
    target_compile_definitions(ssd-sim PRIVATE -DMSCTRL_STANDALONE_DEBUG)
  endif ()
endif ()

# Force version checking in every build
add_dependencies(ssd-sim ${VERSION_TARGET})

# Link libraries
target_link_libraries(ssd-sim
  msctrl
  ${CMAKE_THREAD_LIBS_INIT}
  ${LD_FLAGS}
)
